{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Part 5 - Merge Parcels"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Merge combines two or more parent parcels into one new child parcel. Merge sums up legal areas of parent parcels to the new child parcel legal area. \n",
    "\n",
    "When merge is called with the `record` argument, the Merge function will update the new parcel's `CreatedByRecord` field with the `GlobalID` of the record.  It will also update the original feature's `RetiredByRecord` with the same GlobalID value.\n",
    "\n",
    "This notebook will demonstrate:\n",
    "\n",
    "1. Creating a branch version\n",
    "2. Creating a new parcel record\n",
    "3. Querying for parcels to merge\n",
    "4. Merging the parcels."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "toc": true
   },
   "source": [
    "<h1>Table of Contents<span class=\"tocSkip\"></span></h1>\n",
    "<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#Part-5---Merge-Parcels\" data-toc-modified-id=\"Part-5---Merge-Parcels-1\"><span class=\"toc-item-num\">1&nbsp;&nbsp;</span>Part 5 - Merge Parcels</a></span><ul class=\"toc-item\"><li><ul class=\"toc-item\"><li><ul class=\"toc-item\"><li><span><a href=\"#Create-a-new-branch-version\" data-toc-modified-id=\"Create-a-new-branch-version-1.0.0.1\"><span class=\"toc-item-num\">1.0.0.1&nbsp;&nbsp;</span>Create a new branch version</a></span></li><li><span><a href=\"#Create-a-new-parcel-record\" data-toc-modified-id=\"Create-a-new-parcel-record-1.0.0.2\"><span class=\"toc-item-num\">1.0.0.2&nbsp;&nbsp;</span>Create a new parcel record</a></span></li><li><span><a href=\"#Query-parcels-to-merge\" data-toc-modified-id=\"Query-parcels-to-merge-1.0.0.3\"><span class=\"toc-item-num\">1.0.0.3&nbsp;&nbsp;</span>Query parcels to merge</a></span></li><li><span><a href=\"#Merge-the-parcels\" data-toc-modified-id=\"Merge-the-parcels-1.0.0.4\"><span class=\"toc-item-num\">1.0.0.4&nbsp;&nbsp;</span>Merge the parcels</a></span></li><li><span><a href=\"#Recocile,-Post-and-Delete-the-version\" data-toc-modified-id=\"Recocile,-Post-and-Delete-the-version-1.0.0.5\"><span class=\"toc-item-num\">1.0.0.5&nbsp;&nbsp;</span>Recocile, Post and Delete the version</a></span></li></ul></li><li><span><a href=\"#API-Ref-Documentation\" data-toc-modified-id=\"API-Ref-Documentation-1.0.1\"><span class=\"toc-item-num\">1.0.1&nbsp;&nbsp;</span>API Ref Documentation</a></span></li></ul></li></ul></li></ul></div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "from arcgis import GIS\n",
    "from arcgis.features.layer import FeatureLayer\n",
    "from arcgis.features.layer import FeatureLayerCollection"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "base_server_url = (\n",
    "    \"https://rextapilnx02eb.mysite.com/server/rest/services/WashingtonCounty/\"\n",
    ")\n",
    "gis = GIS(\n",
    "    \"https://myenterprisesite.com/portal/\",\n",
    "    \"my_user_name\",\n",
    "    \"my_secure_password\",\n",
    "    verify_cert=True,\n",
    ")\n",
    "\n",
    "# generate the enpoint urls for feature server, version management and parcel fabric\n",
    "service_endpoints = [\"FeatureServer\", \"ParcelFabricServer\", \"VersionManagementServer\"]\n",
    "service_urls = {url: base_server_url + url for url in service_endpoints}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create a new branch version\n",
    "\n",
    "Get access to the VersionManager object through the Parcel Fabric FeatureLayerCollection"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'my_user_name.merge_version_1'"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "parcel_fabric_flc = FeatureLayerCollection(service_urls[\"FeatureServer\"], gis)\n",
    "version_management_server = parcel_fabric_flc.versions\n",
    "\n",
    "new_version_name = \"merge_version_1\"\n",
    "\n",
    "version = version_management_server.create(new_version_name)\n",
    "fq_version_name = version[\"versionInfo\"][\"versionName\"]\n",
    "fq_version_name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create a new parcel record\n",
    "This is the name of the legal record which states two parcels should be combined into one."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'addResults': [{'objectId': 926,\n",
       "   'globalId': '{40B34F8E-BE24-4936-9A38-02C777AEE348}',\n",
       "   'success': True}],\n",
       " 'updateResults': [],\n",
       " 'deleteResults': []}"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Get the URL of the Records feature layer\n",
    "records_fl_url = [n for n in parcel_fabric_flc.layers if n.properties.name == \"Records\"]\n",
    "records_fl_url = records_fl_url[0].url\n",
    "\n",
    "# A name for the new record\n",
    "new_record_name = \"ParcelRecord001\"\n",
    "\n",
    "# Record information with empty geometry.  The geometry is created during Build\n",
    "record_dict = {\"attributes\": {\"name\": new_record_name}, \"geometry\": None}\n",
    "\n",
    "# Access the Records FeatureLayer object\n",
    "records_fl = FeatureLayer(records_fl_url, gis)\n",
    "\n",
    "# Create the new record within the new branch version\n",
    "new_record = records_fl.edit_features(adds=[record_dict], gdb_version=fq_version_name)\n",
    "\n",
    "new_record"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'{40B34F8E-BE24-4936-9A38-02C777AEE348}'"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Get the globalID of the new record\n",
    "record_globalid = new_record[\"addResults\"][0][\"globalId\"]\n",
    "record_globalid"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Query parcels to merge\n",
    "\n",
    "The [Merge](/python/latest/api-reference/arcgis.features.managers.html#arcgis.features._parcel.ParcelFabricManager.merge) function takes an argument of parcel features in the following format:\n",
    "\n",
    "```\n",
    "[{\"id\":\"<parcelguid>\",\"layerId\":\"<layer_id>\"},{...}]\n",
    "```\n",
    "\n",
    "This can be accomplished by identifying the layer ID of the parcels and querying the parcels by their name (PIN, APN, etc) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "# parcels to be queried and merged\n",
    "parcel_apns = [\"0031001\", \"0031002\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"NAME in ('0031001','0031002')\""
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# build up a WHERE IN clause to search for parcels\n",
    "where_clause = \"NAME in (\"\n",
    "for p in range(len(parcel_apns)):\n",
    "    where_clause += f\"'{str(parcel_apns[p])}',\"\n",
    "where_clause = where_clause[:-1] + \")\"\n",
    "\n",
    "where_clause"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Get the URL of the parcel polygon feature layer\n",
    "parcel_fl_props = [n for n in parcel_fabric_flc.layers if n.properties.name == \"Tax_PF\"]\n",
    "parcels_fl_url = parcel_fl_props[0].url\n",
    "\n",
    "# Get the parcels feature layer\n",
    "parcels_fl = FeatureLayer(parcels_fl_url, gis)\n",
    "\n",
    "# query the features using the WHERE clause\n",
    "# use to_dict() to get access to attributes\n",
    "parcels_subset = parcels_fl.query(\n",
    "    where=where_clause,\n",
    "    out_fields=[\"GlobalID\"],\n",
    "    gdb_version=fq_version_name,\n",
    ")\n",
    "\n",
    "parcels_subset_dict = parcels_subset.to_dict()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(77, '{F736D9F3-DFD9-4FEE-A2E8-07352E74EBDF}'), (79, '{13CD345B-7C4F-41C7-A22D-24E4C6670D85}')]\n"
     ]
    }
   ],
   "source": [
    "print(\n",
    "    [\n",
    "        (a[\"attributes\"][\"objectid\"], a[\"attributes\"][\"globalid\"])\n",
    "        for a in parcels_subset_dict[\"features\"]\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [],
   "source": [
    "map = gis.map(\"Keowns, WI\")\n",
    "map.content.draw(parcels_subset)\n",
    "map.zoom_to_layer(parcels_subset)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=></img>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Two parcels to be merged:\n",
    "map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'id': '{F736D9F3-DFD9-4FEE-A2E8-07352E74EBDF}', 'layerId': 15},\n",
       " {'id': '{13CD345B-7C4F-41C7-A22D-24E4C6670D85}', 'layerId': 15}]"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Generate a list of dicts of {id: <globalid>, layerid: <layer_id>}\n",
    "parcels_to_merge = []\n",
    "\n",
    "parcels_fl_layerid = parcel_fl_props[0].properties.id\n",
    "\n",
    "for item in parcels_subset_dict[\"features\"]:\n",
    "    parcels_to_merge.append(\n",
    "        {\"id\": item[\"attributes\"][\"globalid\"], \"layerId\": parcels_fl_layerid}\n",
    "    )\n",
    "\n",
    "parcels_to_merge"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Merge the parcels\n",
    "- Create a simple dict of feature attributes to update merged parcel (optional)\n",
    "- Start an edit session in the new version\n",
    "- Get the ParcelManager object\n",
    "- Pass the required arguments to the ParcelManager.merge function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from arcgis.features._parcel import ParcelFabricManager\n",
    "\n",
    "# Values to update the merged parcel attributes\n",
    "merged_parcel_name = \"0033004\"\n",
    "attributes = {\n",
    "    \"type\": \"PropertySet\",\n",
    "    \"propertySetItems\": [\"name\", merged_parcel_name, \"statedarea\", 18002],\n",
    "}\n",
    "\n",
    "version_management_server.purge(fq_version_name)\n",
    "# Start the edit session, get the ParcelFabricManager and merge\n",
    "with version_management_server.get(fq_version_name, \"read\") as version:\n",
    "    parcel_fabric = ParcelFabricManager(\n",
    "        service_urls[\"ParcelFabricServer\"], gis, version, parcel_fabric_flc\n",
    "    )\n",
    "\n",
    "    merge = parcel_fabric.merge(\n",
    "        parent_parcels=parcels_to_merge,\n",
    "        merge_record=record_globalid,\n",
    "        target_parcel_type=15,\n",
    "        default_area_unit=109405,\n",
    "        merge_into=\"{00000000-0000-0000-0000-000000000000}\",\n",
    "        attribute_overrides=attributes,\n",
    "        future=False,\n",
    "    )\n",
    "merge[\"success\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Name: None\n",
      "Stated Area: None\n"
     ]
    }
   ],
   "source": [
    "tax_service_edits = [se for se in merge.get(\"serviceEdits\") if se[\"id\"] == 15][0]\n",
    "merged_feature = (\n",
    "    tax_service_edits.get(\"editedFeatures\").get(\"adds\")[0].get(\"attributes\", {})\n",
    ")\n",
    "print(\"Name:\", merged_feature.get(\"Name\"))\n",
    "print(\"Stated Area:\", merged_feature.get(\"StatedArea\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [],
   "source": [
    "merged_parcel = parcels_fl.query(\n",
    "    gdb_version=fq_version_name, where=f\"Name = '{merged_parcel_name}'\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [],
   "source": [
    "map1 = gis.map(\"Keowns, WI\")\n",
    "map1.content.draw(merged_parcel)\n",
    "map1.zoom_to_layer(merged_parcel)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=></img>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Two features merged into one\n",
    "map1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Recocile, Post and Delete the version\n",
    "\n",
    "When editing is complete, the new features can be posted from the new branch version to the default version.  In this workflow, Reconcile must occur first.  Once posted, the version can optionally be deleted."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "version = version_management_server.get(f\"my_user_name.{new_version_name}\")\n",
    "version.reconcile()\n",
    "version.post()\n",
    "version.delete()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## API Ref Documentation\n",
    "\n",
    "- [ArcGIS Python API - Version Manager](/python/latest/api-reference/arcgis.features.managers.html#versionmanager)\n",
    "- [ArcGIS Python API - Parcel Fabric Manager](/python/latest/api-reference/arcgis.features.managers.html#parcelfabricmanager)\n",
    "- [ArcGIS REST API - VersionManagementServer](/rest/services-reference/enterprise/version-management-service.htm)\n",
    "- [ArcGIS REST API - ParcelFabricServer](/rest/services-reference/enterprise/overview-of-parcel-fabric-sevices.htm)\n",
    "- [ArcGIS Pro - Branch Versioning Scenarios](https://pro.arcgis.com/en/pro-app/latest/help/data/geodatabases/overview/branch-version-scenarios.htm)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.0"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": true,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  },
  "vscode": {
   "interpreter": {
    "hash": "ce21b655b4d6c9e397d5ad93d5666c623f49909f6d0cc2f72076dafcf1b3ecfb"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
